package labox.innovation.gameserver.model.actor;

import java.util.Collection;
import java.util.List;

import javolution.util.FastList;
import labox.innovation.config.Config;
import labox.innovation.gameserver.ai.CtrlIntention;
import labox.innovation.gameserver.cache.HtmCache;
import labox.innovation.gameserver.datatables.ItemTable;
import labox.innovation.gameserver.datatables.SkillTable;
import labox.innovation.gameserver.instancemanager.QuestManager;
import labox.innovation.gameserver.model.FuzeDropCategory;
import labox.innovation.gameserver.model.FuzeDropData;
import labox.innovation.gameserver.model.FuzeTrusteeInstance;
import labox.innovation.gameserver.model.FuzeWorld;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.L2Spawn;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.instance.FuzeWarehouseInstance;
import labox.innovation.gameserver.model.actor.instance.L2MerchantInstance;
import labox.innovation.gameserver.model.actor.instance.L2SummonInstance;
import labox.innovation.gameserver.model.actor.knownlist.NpcKnownList;
import labox.innovation.gameserver.model.actor.stat.NpcStat;
import labox.innovation.gameserver.model.actor.status.NpcStatus;
import labox.innovation.gameserver.model.entity.L2Event;
import labox.innovation.gameserver.model.itemcontainer.NpcInventory;
import labox.innovation.gameserver.model.quest.Quest;
import labox.innovation.gameserver.model.quest.QuestState;
import labox.innovation.gameserver.model.quest.State;
import labox.innovation.gameserver.network.L2GameClient;
import labox.innovation.gameserver.network.SystemMessageId;
import labox.innovation.gameserver.network.serverpackets.AbstractNpcInfo;
import labox.innovation.gameserver.network.serverpackets.ActionFailed;
import labox.innovation.gameserver.network.serverpackets.NpcHtmlMessage;
import labox.innovation.gameserver.network.serverpackets.ServerObjectInfo;
import labox.innovation.gameserver.network.serverpackets.StatusUpdate;
import labox.innovation.gameserver.network.serverpackets.SystemMessage;
import labox.innovation.gameserver.skills.Stats;
import labox.innovation.gameserver.taskmanager.DecayTaskManager;
import labox.innovation.gameserver.templates.chars.NpcTemplate;
import labox.innovation.gameserver.templates.item.L2Item;
import labox.innovation.gameserver.templates.item.L2Weapon;
import labox.innovation.gameserver.util.StringUtil;
import labox.innovation.gameserver.util.Util;

/**
 * This class represents a Non-Player-Character in the world. It can be a monster or a friendly character. It also uses a template to fetch some static values. The templates are hardcoded in the client, so we can rely on them.<BR>
 * <BR>
 * 
 * L2Character :<BR>
 * <BR>
 * <li>L2Attackable</li> <li>L2BoxInstance</li> <li>L2FolkInstance</li>
 * 
 * @version $Revision: 1.32.2.7.2.24 $ $Date: 2005/04/11 10:06:09 $
 */
public class L2Npc extends L2Character {
	//private static Logger _log = Logger.getLogger(L2NpcInstance.class.getName());

	/** The interaction distance of the L2NpcInstance(is used as offset in MovetoLocation method) */
	public static final int INTERACTION_DISTANCE = 150;

	/** The L2Spawn object that manage this L2NpcInstance */
	private L2Spawn _spawn;

	private NpcInventory _inventory;

	/** The flag to specify if this L2NpcInstance is busy */
	private boolean _isBusy;

	/** The busy message for this L2NpcInstance */
	private String _busyMessage = "";

	/** True if endDecayTask has already been called */
	volatile boolean _isDecayed;

	public boolean isEventMob;

	private boolean _isInTown;

	/**
	 * Constructor of L2NpcInstance (use L2Character constructor).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Call the L2Character constructor to set the _template of the L2Character (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)</li> <li>Set the name of the L2Character</li> <li>Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it</li><BR>
	 * <BR>
	 * 
	 * @param objectId
	 *            Identifier of the object to initialized
	 * @param template
	 *            The L2NpcTemplate to apply to the NPC
	 * 
	 */
	public L2Npc(long objectId, NpcTemplate template) {
		// Call the L2Character constructor to set the _template of the L2Character, copy skills from template to object
		// and link _calculators to NPC_STD_CALCULATOR
		super(objectId, template);
		//		initCharStatusUpdateValues();

		if (template == null) {
			_log.error("No template for Npc. Please check your datapack is setup correctly.", Config.SERVER_ID);
			return;
		}

		// Set the name of the L2Character
		setName(template.name);
		//		if ((template.ss > 0 || template.bss > 0) && template.ssRate > 0)
		//			_inventory = new NpcInventory(this);
	}

	@Override
	public NpcKnownList getKnownList() {
		return (NpcKnownList) _knownList;
	}

	@Override
	public void initKnownList() {
		setKnownList(new NpcKnownList(this));
	}

	@Override
	public NpcStat getStat() {
		return (NpcStat) super.getStat();
	}

	@Override
	public void initCharStat() {
		setStat(new NpcStat(this));
	}

	@Override
	public NpcStatus getStatus() {
		return (NpcStatus) _status;
	}

	@Override
	public void initCharStatus() {
		setStatus(new NpcStatus(this));
	}

	/** Return the L2NpcTemplate of the L2NpcInstance. */
	@Override
	public final NpcTemplate getTemplate() {
		return (NpcTemplate) _template;
	}

	/**
	 * Return the generic Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR>
	 * <BR>
	 */
	public int getNpcId() {
		return getTemplate()._id;
	}

	@Override
	public boolean isAttackable() {
		return getTemplate()._attackable;
	}

	/**
	 * Return the Level of this L2NpcInstance contained in the L2NpcTemplate.<BR>
	 * <BR>
	 */
	@Override
	public final int getLevel() {
		return getTemplate().level;
	}

	/**
	 * 是否随机走动
	 * 
	 * @return
	 */
	public final boolean isNoRndWalk() {
		return getTemplate()._isNoRndWalk;
	}

	/**
	 * Return True if the L2NpcInstance is agressive (ex : L2MonsterInstance in function of aggroRange).<BR>
	 * <BR>
	 */
	public boolean isAggressive() {
		return false;
	}

	/**
	 * Return the Aggro Range of this L2NpcInstance contained in the L2NpcTemplate.<BR>
	 * <BR>
	 */
	public int getAggroRange() {
		return getTemplate().aggroRange;
	}

	/**
	 * Send a packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance.<BR>
	 * <BR>
	 */
	@Override
	public void updateAbnormalEffect() {
		// Send a Server->Client packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
		Collection<FuzePcInstance> plrs = getKnownList().getKnownPlayers().values();
		//		for (L2PcInstance player : plrs) {
		//			if (getRunSpeed() == 0)
		//				player.sendPacket(new ServerObjectInfo(this, player));
		//			else
		//				player.sendPacket(new AbstractNpcInfo.NpcInfo(this, player));
		//		}
	}

	/**
	 * Return False.<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2MonsterInstance : Check if the attacker is not another L2MonsterInstance</li> <li>L2PcInstance</li><BR>
	 * <BR>
	 */
	@Override
	public boolean isAutoAttackable(L2Character attacker) {
		return false;
	}

	/**
	 * Return the busy status of this L2NpcInstance.<BR>
	 * <BR>
	 */
	public final boolean isBusy() {
		return _isBusy;
	}

	/**
	 * Set the busy status of this L2NpcInstance.<BR>
	 * <BR>
	 */
	public void setBusy(boolean isBusy) {
		_isBusy = isBusy;
	}

	/**
	 * Return the busy message of this L2NpcInstance.<BR>
	 * <BR>
	 */
	public final String getBusyMessage() {
		return _busyMessage;
	}

	/**
	 * Set the busy message of this L2NpcInstance.<BR>
	 * <BR>
	 */
	public void setBusyMessage(String message) {
		_busyMessage = message;
	}

	/**
	 * Return true if this L2Npc instance can be warehouse manager.<BR>
	 * <BR>
	 */
	public boolean isWarehouse() {
		return false;
	}

	protected boolean canTarget(FuzePcInstance player) {
		if (player.isOutOfControl()) {
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return false;
		}
		if (player.isLockedTarget() && player.getLockedTarget() != this) {
			player.sendPacket(new SystemMessage(SystemMessageId.FAILED_CHANGE_TARGET));
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return false;
		}

		return true;
	}

	/**
	 * 检查可以和这个NPC交互的条件是否满足
	 * 
	 * @param player
	 * @return
	 */
	public boolean canInteract(FuzePcInstance player) {
		// TODO: NPC busy check etc...

		if (player.isDead())
			return false;
		if (Util.FastDistance2D(player.getX(), player.getY(), getX(), getY()) > INTERACTION_DISTANCE)
			return false;
		if (player.getInstanceId() != getInstanceId() && player.getInstanceId() != -1)
			return false;

		return true;
	}

	/**
	 * Manage actions when a player click on the L2NpcInstance.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions on first click on the L2NpcInstance (Select it)</U> :</B><BR>
	 * <BR>
	 * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP bar</li> <li>Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client</li><BR>
	 * <BR>
	 * 
	 * <B><U> Actions on second click on the L2NpcInstance (Attack it/Intercat with it)</U> :</B><BR>
	 * <BR>
	 * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2NpcInstance is autoAttackable, notify the L2PcInstance AI with AI_INTENTION_ATTACK (after a height verification)</li> <li>If L2NpcInstance is NOT autoAttackable, notify the L2PcInstance AI with AI_INTENTION_INTERACT (after a distance verification) and show message</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid that client wait an other packet</B></FONT><BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Client packet : Action, AttackRequest</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2ArtefactInstance : Manage only fisrt click to select Artefact</li><BR>
	 * <BR>
	 * <li>L2GuardInstance :</li><BR>
	 * <BR>
	 * 
	 * @param player
	 *            The L2PcInstance that start an action on the L2NpcInstance
	 * 
	 */
	@Override
	public void onAction(FuzePcInstance player) {
		if (!canTarget(player))
			return;

		player.setLastFolkNPC(this);

		// Check if the L2PcInstance already target the L2NpcInstance
		if (this != player.getTarget()) {
			if (_log.isDebugEnabled())
				_log.debug("new target selected:" + getObjectId());

			// Set the target of the L2PcInstance player
			player.setTarget(this);

			// Check if the player is attackable (without a forced attack)
			if (isAttackable()) {// 如果是一个可以自动攻击的类型，则更新HP
				// Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
				StatusUpdate su = new StatusUpdate(getObjectId());
				su.addAttribute(StatusUpdate.HP, getCurrentHpRadio());
				player.sendPacket(su);
			}
		}

		if (isAttackable()) {// 如果是一个可以可以自动攻击的设置L2PcInstance开始进攻这个NPC
			if (!isDead()) {
				player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);// 设置player进攻这个NPC
			}
		} else {
			if (canInteract(player)) {
				// Open a chat window on client with the text of the L2NpcInstance
				if (isEventMob) {
					L2Event.showEventHtml(player, String.valueOf(getObjectId()));
				} else {
					Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
					if ((qlsa != null) && qlsa.length > 0)
						player.setLastQuestNpcObject(getObjectId());
					Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.ON_FIRST_TALK);
					if ((qlst != null) && qlst.length == 1)
						qlst[0].notifyFirstTalk(this, player);
					else
						showChatWindow(player, 0);// 默认对话界面
				}
			}
		}

	}

	/**
	 * Manage and Display the GM console to modify the L2NpcInstance (GM only).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions (If the L2PcInstance is a GM only)</U> :</B><BR>
	 * <BR>
	 * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP bar</li> <li>Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid that client wait an other packet</B></FONT><BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Client packet : Action</li><BR>
	 * <BR>
	 * 
	 * @param client
	 *            The thread that manage the player that pessed Shift and click on the L2NpcInstance
	 * 
	 */
	@Override
	public void onActionShift(L2GameClient client) {
		// Get the L2PcInstance corresponding to the thread
		FuzePcInstance player = client.getActiveChar();
		if (player == null)
			return;

		// Check if the L2PcInstance is a GM
		if (player.getAccessLevel().isGm()) {
			// Set the target of the L2PcInstance player
			player.setTarget(this);

			// Check if the player is attackable (without a forced attack)
			if (isAutoAttackable(player)) {
				// Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
				StatusUpdate su = new StatusUpdate(getObjectId());
				su.addAttribute(StatusUpdate.HP, (int) getCurrentHpRadio());
				player.sendPacket(su);
			}

			// Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance
			//			NpcHtmlMessage html = new NpcHtmlMessage(0);
			//			String className = getClass().getName().substring(43);
			//			final StringBuilder html1 = StringUtil.startAppend(500, "<html><body><center><font color=\"LEVEL\">NPC Information</font></center>" + "<br>" + "Instance Type: ", className, "<br1>Faction: ", getFactionId() != null ? getFactionId() : "null", "<br1>");
			//			StringUtil.append(html1, "Coords ", String.valueOf(getX()), ",", String.valueOf(getY()), "<br1>");
			//			if (getSpawn() != null)
			//				StringUtil.append(html1, "Spawn ", String.valueOf(getSpawn().getLocx()), ",", String.valueOf(getSpawn().getLocy()), " Loc ID: ", String.valueOf(getSpawn().getLocation()), "<br1>", "Distance from spawn 2D ", String.valueOf(getDistance(getSpawn().getLocx(), getSpawn().getLocy())), "<br1>");
			//
			//			
			//			StringUtil.append(html1, "Respawn Time: ", (getSpawn() != null ? String.valueOf(getSpawn().getRespawnDelay() / 1000) : "?"), "  Seconds<br>");
			//			
			//
			//			StringUtil.append(html1, "<table border=\"0\" width=\"100%\">" + "<tr><td>Object ID</td><td>", String.valueOf(getObjectId()), "</td><td>NPC ID</td><td>", String.valueOf(getTemplate().id), "</td></tr>", "</td><td>AI </td><td>", (hasAI() ? String.valueOf(getAI().getIntention().name()) : "NULL"), "</td></tr>" + "<tr><td>Level</td><td>", String.valueOf(getLevel()), "</td><td>Aggro</td><td>", String.valueOf((this instanceof L2Attackable) ? ((L2Attackable) this).getAggroRange() : 0), "</td></tr>" + "</table><br>" + "<font color=\"LEVEL\">Combat</font>" + "<table border=\"0\" width=\"100%\">" + "<tr><td>Current HP</td><td>", String.valueOf(getCurrentHp()), "</td><td>Current MP</td><td>", String.valueOf(getCurrentMp()), "</td></tr>" + "<tr><td>Max.HP</td><td>", String.valueOf((int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null))), "*", String.valueOf((int) (getStat().calcStat(Stats.MAX_HP, 1, this, null))), "</td><td>Max.MP</td><td>", String.valueOf(getMaxMp()),
			//					"</td></tr>" + "<tr><td>P.Atk.</td><td>", String.valueOf(getPAtk(null)), "</td><td>M.Atk.</td><td>", String.valueOf(getMAtk(null, null)), "</td></tr>" + "<tr><td>P.Def.</td><td>", String.valueOf(getPDef(null)), "</td><td>M.Def.</td><td>", String.valueOf(getMDef(null, null)), "</td></tr>" + "<tr><td>Accuracy</td><td>" + String.valueOf(getAccuracy()), "</td><td>Evasion</td><td>", String.valueOf(getEvasionRate(null)), "</td></tr>" + "<tr><td>Critical</td><td>", String.valueOf(getCriticalHit(null, null)), "</td><td>Speed</td><td>", String.valueOf(getRunSpeed()), "</td></tr>" + "<tr><td>Atk.Speed</td><td>", String.valueOf(getPAtkSpd()), "</td><td>Cast.Speed</td><td>", String.valueOf(getMAtkSpd()), "</td></tr>" + "</table><br>" + "<font color=\"LEVEL\">Basic Stats</font>" + "<table border=\"0\" width=\"100%\">" + "<tr><td>STR</td><td>", String.valueOf(getSTR()), "</td><td>DEX</td><td>", String.valueOf(getDEX()), "</td><td>CON</td><td>", String.valueOf(getCON()),
			//					"</td></tr>" + "<tr><td>INT</td><td>", String.valueOf(getINT()), "</td><td>WIT</td><td>", String.valueOf(getWIT()), "</td><td>MEN</td><td>", String.valueOf(getMEN()), "</td></tr>" + "</table>" + "<br><center><table><tr><td><button value=\"Edit NPC\" action=\"bypass -h admin_edit_npc ", String.valueOf(getTemplate().id), "\" width=100 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"><br1></td>" + "<td><button value=\"Kill\" action=\"bypass -h admin_kill\" width=40 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td><br1></tr>" + "<tr><td><button value=\"Show DropList\" action=\"bypass -h admin_show_droplist ", String.valueOf(getTemplate().id), "\" width=100 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td></tr>" + "<td><button value=\"Delete\" action=\"bypass -h admin_delete\" width=40 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td></tr>" + "</table></center><br>"
			//							+ "</body></html>");
			//
			//			html.setHtml(html1.toString());
			//			player.sendPacket(html);
		} else if (Config.ALT_GAME_VIEWNPC) {
			// Set the target of the L2PcInstance player
			player.setTarget(this);

			// Check if the player is attackable (without a forced attack)
			if (isAutoAttackable(player)) {
				// Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
				StatusUpdate su = new StatusUpdate(getObjectId());
				su.addAttribute(StatusUpdate.HP, (int) getCurrentHp());
				player.sendPacket(su);
			}

			NpcHtmlMessage html = new NpcHtmlMessage(0);
			final StringBuilder html1 = StringUtil.startAppend(1000, "<html><body>" + "<br><center><font color=\"LEVEL\">[Combat Stats]</font></center>" + "<table border=0 width=\"100%\">" + "<tr><td>Max.HP</td><td>", String.valueOf((int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null))), "*", String.valueOf((int) getStat().calcStat(Stats.MAX_HP, 1, this, null)), "</td><td>Max.MP</td><td>", String.valueOf(getMaxMp()), "</td></tr>" + "<tr><td>P.Atk.</td><td>", String.valueOf(getPAtk()), "</td><td>M.Atk.</td><td>", String.valueOf(getMAtk()), "</td></tr>" + "<tr><td>P.Def.</td><td>", String.valueOf(getPDef()), "</td><td>M.Def.</td><td>", String.valueOf(getMDef()), "</td></tr>" + "<tr><td>Accuracy</td><td>", String.valueOf(getAccuracy()), "</td><td>Evasion</td><td>", String.valueOf(getEvasionRate()), "</td></tr>" + "<tr><td>Critical</td><td>", String.valueOf(getCriticalHit()), "</td><td>Speed</td><td>", String.valueOf(getRunSpeed()), "</td></tr>"
					+ "<tr><td>Atk.Speed</td><td>", String.valueOf(getPAtkSpd()), "</td></tr>" + "<tr><td>Race</td><td>", getTemplate().biotype.toString(), "</td><td></td><td></td></tr>" + "</table>" + "<br><center><font color=\"LEVEL\">[Basic Stats]</font></center>" + "<table border=0 width=\"100%\">" + "<tr><td>STR</td><td>", String.valueOf(getSTR()), "</td><td>DEX</td><td>", String.valueOf(getDEX()), "</td><td>CON</td><td>", String.valueOf(getCON()), "</td></tr>" + "<tr><td>INT</td><td>", String.valueOf(getINT()), "</td><td>MEN</td><td>", String.valueOf(getMEN()), "</td></tr>" + "</table>");

			if (getTemplate().getDropData() != null) {
				StringUtil.append(html1, "<br><center><font color=\"LEVEL\">[Drop Info]</font></center>" + "<br>Rates legend: <font color=\"ff0000\">50%+</font> <font color=\"00ff00\">30%+</font> <font color=\"0000ff\">less than 30%</font>" + "<table border=0 width=\"100%\">");
				for (FuzeDropCategory cat : getTemplate().getDropData()) {
					for (FuzeDropData drop : cat.getAllDrops()) {
						final L2Item item = ItemTable.getInstance().getTemplate(drop.getItemId());
						if (item == null)
							continue;

						final String color;

						if (drop.getChance() >= 500000)
							color = "ff0000";
						else if (drop.getChance() >= 300000)
							color = "00ff00";
						else
							color = "0000ff";

						StringUtil.append(html1, "<tr><td><font color=\"", color, "\">", item.getName(), "</font></td><td>", (drop.isQuestDrop() ? "Quest" : "Drop"), "</td></tr>");
					}
				}
				html1.append("</table>");
			}
			html1.append("</body></html>");

			html.setHtml(html1.toString());
			player.sendPacket(html);
		}

		// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
		player.sendPacket(ActionFailed.STATIC_PACKET);
	}

	public final boolean getIsInTown() {
		return _isInTown;
	}

	/**
	 * Open a quest or chat window on client with the text of the L2NpcInstance in function of the command.<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Client packet : RequestBypassToServer</li><BR>
	 * <BR>
	 * 
	 * @param command
	 *            The command string received from client
	 * 
	 */
	public void onBypassFeedback(FuzePcInstance player, String command) {
		if (isBusy() && getBusyMessage().length() > 0) {
			player.sendPacket(ActionFailed.STATIC_PACKET);

			NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
			html.setFile("data/html/npcbusy.htm");
			html.replace("%busymessage%", getBusyMessage());
			html.replace("%npcname%", getName());
			html.replace("%playername%", player.getName());
			player.sendPacket(html);
		} else if (command.startsWith("Quest")) {// npc对话框中显示任务列表
			String quest = "";
			try {
				quest = command.substring(5).trim();
			} catch (IndexOutOfBoundsException ioobe) {
			}
			if (quest.length() == 0)
				showQuestWindow(player);
			else
				showQuestWindow(player, quest);
		} else if (command.startsWith("Chat")) {// 显示npc对话框
			int val = 0;
			try {
				val = Integer.parseInt(command.substring(5));
			} catch (IndexOutOfBoundsException ioobe) {
			} catch (NumberFormatException nfe) {
			}
			showChatWindow(player, val);
		} else if (command.startsWith("Link")) {
			String path = command.substring(5).trim();
			if (path.indexOf("..") != -1)
				return;
			String filename = "data/html/" + path;
			NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
			html.setFile(filename);
			html.replace("%objectId%", String.valueOf(getObjectId()));
			player.sendPacket(html);
		} else if (command.startsWith("CPRecovery")) {
			makeCPRecovery(player);
		} else if (command.startsWith("SupportMagicServitor")) {
			makeSupportMagic(player, true);
		} else if (command.startsWith("SupportMagic")) {
			makeSupportMagic(player, false);
		} else if (command.startsWith("GiveBlessing")) {
			giveBlessingSupport(player);
		}
		//		else if (command.startsWith("Augment")) {
		//			int cmdChoice = Integer.parseInt(command.substring(8, 9).trim());
		//			switch (cmdChoice) {
		//			case 1:
		//				player.sendPacket(new SystemMessage(SystemMessageId.SELECT_THE_ITEM_TO_BE_AUGMENTED));
		//				player.sendPacket(new ExShowVariationMakeWindow());
		//				break;
		//			case 2:
		//				player.sendPacket(new SystemMessage(SystemMessageId.SELECT_THE_ITEM_FROM_WHICH_YOU_WISH_TO_REMOVE_AUGMENTATION));
		//				player.sendPacket(new ExShowVariationCancelWindow());
		//				break;
		//			}
		//		} else if (command.startsWith("ReleaseAttribute")) {
		//			player.sendPacket(new ExShowBaseAttributeCancelWindow(player));
		//		}

	}

	/**
	 * Return null (regular NPCs don't have weapons instancies).<BR>
	 * <BR>
	 */
	@Override
	public L2ItemInstance getActiveWeaponInstance() {
		// regular NPCs dont have weapons instancies
		return null;
	}

	/**
	 * Return the weapon item equiped in the right hand of the L2NpcInstance or null.<BR>
	 * <BR>
	 */
	@Override
	public L2Weapon getActiveWeaponItem() {
		return null;
	}

	public void giveBlessingSupport(FuzePcInstance player) {
		if (player == null)
			return;

		// Blessing of protection - author kerberos_20. Used codes from Rayan - L2Emu project.
		// Prevent a cursed weapon weilder of being buffed - I think no need of that becouse karma check > 0
		// if (player.isCursedWeaponEquiped()) 
		//   return; 

		int player_level = player.getLevel();
		// Select the player 
		setTarget(player);
		// If the player is too high level, display a message and return 
		if (player_level > 39 /*|| player.getClassId().level() >= 2*/) {
			String content = "<html><body>Newbie Guide:<br>I'm sorry, but you are not eligible to receive the protection blessing.<br1>It can only be bestowed on <font color=\"LEVEL\">characters below level 39 who have not made a seccond transfer.</font></body></html>";
			insertObjectIdAndShowChatWindow(player, content);
			return;
		}
		L2Skill skill = SkillTable.getInstance().getInfo(5182, 1);
		doCast(skill);
	}

	/**
	 * Return null (regular NPCs don't have weapons instancies).<BR>
	 * <BR>
	 */
	@Override
	public L2ItemInstance getSecondaryWeaponInstance() {
		// regular NPCs dont have weapons instancies
		return null;
	}

	/**
	 * Return the weapon item equiped in the left hand of the L2NpcInstance or null.<BR>
	 * <BR>
	 */
	@Override
	public L2Weapon getSecondaryWeaponItem() {
		return null;
	}

	/**
	 * Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance.<BR>
	 * <BR>
	 * 创建ObjectId,并 显示聊天窗口<br>
	 * 
	 * @param player
	 *            The L2PcInstance who talks with the L2NpcInstance
	 * @param content
	 *            The text of the L2NpcMessage
	 * 
	 */
	public void insertObjectIdAndShowChatWindow(FuzePcInstance player, String content) {
		// Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
		content = content.replaceAll("%objectId%", String.valueOf(getObjectId()));// 替换%objectId%
		NpcHtmlMessage npcReply = new NpcHtmlMessage(getObjectId());
		npcReply.setHtml(content);
		player.sendPacket(npcReply);
	}

	/**
	 * Return the pathfile of the selected HTML file in function of the npcId and of the page number.<BR>
	 * <BR>
	 * 
	 * <B><U> Format of the pathfile </U> :</B><BR>
	 * <BR>
	 * <li>if the file exists on the server (page number = 0) : <B>data/html/default/12006.htm</B> (npcId-page number)</li> <li>if the file exists on the server (page number > 0) : <B>data/html/default/12006-1.htm</B> (npcId-page number)</li> <li>if the file doesn't exist on the server : <B>data/html/npcdefault.htm</B> (message : "I have nothing to say to you")</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2GuardInstance : Set the pathfile to data/html/guard/12006-1.htm (npcId-page number)</li><BR>
	 * <BR>
	 * 
	 * @param npcId
	 *            该L2NpcInstance的文字标识,必须显示
	 * @param val
	 *            显示的页码
	 * 
	 */
	public String getHtmlPath(int npcId, int val) {
		String pom = "";

		if (val == 0)
			pom = "" + npcId;
		else
			pom = npcId + "-" + val;

		String temp = "data/xml/default/" + pom + ".xml";

		if (!Config.LAZY_CACHE) {
			// If not running lazy cache the file must be in the cache or it doesnt exist
			// 如果没有运行延迟缓存,该文件必须在缓存或不存在
			if (HtmCache.getInstance().contains(temp))
				return temp;
		} else {
			if (HtmCache.getInstance().isLoadable(temp))
				return temp;
		}

		// If the file is not found, the standard message "I have nothing to say to you" is returned
		return "/data/xml/npcdefault.xml";
	}

	/**
	 * Open a choose quest window on client with all quests available of the L2NpcInstance.<BR>
	 * <BR>
	 * 打开一个有多个任务可选择的对话框<br>
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li><BR>
	 * <BR>
	 * 
	 * @param player
	 *            The L2PcInstance that talk with the L2NpcInstance
	 * @param quests
	 *            The table containing quests of the L2NpcInstance
	 * 
	 */
	public void showQuestChooseWindow(FuzePcInstance player, Quest[] quests) {
		final StringBuilder sb = StringUtil.startAppend(150, "");
		sb.append("<form nm = 'qkrw' tl='" + getName() + "'> “年轻轻轻竟有如此胸怀大志，真是后生可畏，我这儿确实有那么几件事情需要你帮帮忙。”<br/>");
		for (Quest q : quests) {
			QuestState qs = player.getQuestState(q.getScriptName());

			String str = "<a cmd='s:85:bypass -h npc_" + String.valueOf(getObjectId()) + "_Quest " + q.getName() + "' oc='addNetLock()'>" + q.getDescr();
			if (qs != null) {
				if (qs.getState() == State.STARTED && qs.getInt("cond") > 0)
					str += " (已接)";// 正在进行中
				else if (qs.getState() == State.SUCCEED)
					str += " (成功)";// 已经完成任务目标,可交还
				else if (qs.getState() == State.FAILURE)
					str += " (失败)";
				else if (qs.getState() == State.COMPLETED)
					continue;
				// sb.append(" (完成)");
			}
			sb.append(str).append("</a><br/>");
		}

		// sb.append("");

		// Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
		insertObjectIdAndShowChatWindow(player, sb.append("</form>").toString());
	}

	/**
	 * Open a quest window on client with the text of the L2NpcInstance.<BR>
	 * <BR>
	 * 在客户端显示任务窗口 <br>
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Get the text of the quest state in the folder data/scripts/quests/questId/stateId.htm</li> <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet</li><BR>
	 * <BR>
	 * 
	 * @param player
	 *            The L2PcInstance that talk with the L2NpcInstance
	 * @param questId
	 *            The Identifier of the quest to display the message
	 * 
	 */
	public void showQuestWindow(FuzePcInstance player, String questId) {
		String content = null;

		Quest q = QuestManager.getInstance().getQuest(questId);

		// 查看任务状态
		QuestState qs = player.getQuestState(questId);

		if (q == null) {
			// 没有找到任务
			//			if (Config.DEVELOPER)
			//				content = "<form>您已经完成该任务,或者目前你的条件不符合本次npc的最低任务的要求.(或者该脚本没有加载-_-!:)</form><br/>";
			//			else
			//				content = "<form>您已经完成该任务,或者目前你的条件不符合本次npc的最低任务的要求.</form><br/>";

			content = "<form ol='back()' tl='" + getName() + "'>这儿目前没有什么事情需要你的帮助，还是去别处看看吧……</form><br/>";
		} else {
			// if ((q.getQuestIntId() >= 1 && q.getQuestIntId() < 20000) &&
			// player.getInventoryLimit() * 0.8 <=
			// player.getInventory().getSize()) {
			// player.sendPacket(new
			// SystemMessage(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT));
			// return;
			// }

			if (qs == null) {
				
// TODO 这个地方任务最多活动任务不能超过30个，下面屏蔽掉的代码有问题，其抓取的是所有任务不超过30条。待内测以后补充此处逻辑。Frank 2010-07-12
//				if (q.getQuestIntId() >= 1 && q.getQuestIntId() < 20000) {
//					Quest[] questList = player.getAllActiveQuests();
//					if (questList.length >= 30) // 如果有太多正在进行的任务，不显示窗口，并发送消息
//					{
//						player.sendPacket(new SystemMessage(SystemMessageId.TOO_MANY_QUESTS));
//						return;
//					}
//				}
				
				// 检查状态点
				Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);

				if (qlst != null && qlst.length > 0) {
					for (Quest temp : qlst) {
						if (temp == q) {
							qs = q.newQuestState(player);
							break;
						}
					}
				}
			}
		}

		if (qs != null) {
			// 如果任务是已经开始，没有必要显示窗口
			if (!qs.getQuest().notifyTalk(this, qs))
				return;

			questId = qs.getQuest().getName();
			String stateId = State.getStateName(qs.getState());
			String path = "data/scripts/quests/" + questId + "/" + stateId + ".xml";
			content = HtmCache.getInstance().getHtm(path); // TODO path for
			// quests xml

			if (_log.isDebugEnabled()) {
				if (content != null) {
					_log.debug("Showing quest window for quest " + questId + " html path: " + path);
				} else {
					_log.debug("File not exists for quest " + questId + " html path: " + path);
				}
			}
		}

		// Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
		if (content != null)
			insertObjectIdAndShowChatWindow(player, content);

		// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
		// 发送一个静态包到客户端,防止客户端等待
		player.sendPacket(ActionFailed.STATIC_PACKET);
	}

	/**
	 * Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.<BR>
	 * 收集等待任务/启动点，显示QuestChooseWindow（多任务选择窗口,如果有多个可用）或QuestWindow<BR>
	 * 
	 * @param player
	 *            The L2PcInstance that talk with the L2NpcInstance
	 * 
	 */
	public void showQuestWindow(FuzePcInstance player) {
		// collect awaiting quests and start points
		List<Quest> options = new FastList<Quest>();

		QuestState[] awaits = player.getQuestsForTalk(getTemplate()._id);
		Quest[] starts = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);

		// Quests are limited between 1 and 999 because those are the quests that are supported by the client.  
		// By limiting them there, we are allowed to create custom quests at
		// higher IDs without interfering
		// 任务是有限的1和999之间，因为这些都是属于客户端支持任务。
		// 通过限制他们那里，我们可以创造更高的ID不干预定制任务
		if (awaits != null) {
			for (QuestState x : awaits) {
				if (!options.contains(x.getQuest()))
					if ((x.getQuest().getQuestIntId() > 0) && (x.getQuest().getQuestIntId() < 20000))
						options.add(x.getQuest());
			}
		}

		if (starts != null) {
			for (Quest x : starts) {
				if (!options.contains(x))
					if ((x.getQuestIntId() > 0) && (x.getQuestIntId() < 20000))
						options.add(x);
			}
		}

		player.filterNpcQuestList(options);

		// Display a QuestChooseWindow (if several quests are available) or QuestWindow
		if (options.size() > 1) {
			showQuestChooseWindow(player, options.toArray(new Quest[options.size()]));
		} else if (options.size() == 1) {
			showQuestWindow(player, options.get(0).getName());
		} else {
			showQuestWindow(player, "");
		}
	}

	public void makeCPRecovery(FuzePcInstance player) {
		if (getNpcId() != 31225 && getNpcId() != 31226)
			return;

		L2Skill skill = SkillTable.getInstance().getInfo(4380, 1);
		if (skill != null) {
			setTarget(player);
			doCast(skill);
		}
		player.sendPacket(ActionFailed.STATIC_PACKET);
	}

	/**
	 * Add Newbie helper buffs to L2Player according to its level.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Get the range level in wich player must be to obtain buff</li> <li>If player level is out of range, display a message and return</li> <li>According to player level cast buff</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> Newbie Helper Buff list is define in sql table helper_buff_list</B></FONT><BR>
	 * <BR>
	 * 
	 * @param player
	 *            The L2PcInstance that talk with the L2NpcInstance
	 * 
	 */
	public void makeSupportMagic(FuzePcInstance player, boolean isSummon) {
		if (player == null)
			return;

		int player_level = player.getLevel();
		int lowestLevel = 0;
		int highestLevel = 0;

		if (isSummon) {
			if (player.getPet() == null || !(player.getPet() instanceof L2SummonInstance)) {
				String content = "<html><body>Only servitors can receive this Support Magic. If you do not have a servitor, you cannot access these spells.</body></html>";
				insertObjectIdAndShowChatWindow(player, content);
				return;
			}
			setTarget(player.getPet());
		} else
			// 	Select the player
			setTarget(player);

		//		if (isSummon) {
		//			lowestLevel = HelperBuffTable.getInstance().getServitorLowestLevel();
		//			highestLevel = HelperBuffTable.getInstance().getServitorHighestLevel();
		//		} else {
		//			// 	Calculate the min and max level between which the player must be to obtain buff
		//			if (player.isMageClass()) {
		//				lowestLevel = HelperBuffTable.getInstance().getMagicClassLowestLevel();
		//				highestLevel = HelperBuffTable.getInstance().getMagicClassHighestLevel();
		//			} else {
		//				lowestLevel = HelperBuffTable.getInstance().getPhysicClassLowestLevel();
		//				highestLevel = HelperBuffTable.getInstance().getPhysicClassHighestLevel();
		//			}
		//		}
		// If the player is too high level, display a message and return
		if (player_level > highestLevel) {
			String content = "<html><body>Newbie Guide:<br>Only a <font color=\"LEVEL\">novice character of level " + highestLevel + " or less</font> can receive my support magic.<br>Your novice character is the first one that you created and raised in this world.</body></html>";
			insertObjectIdAndShowChatWindow(player, content);
			return;
		}

		// If the player is too low level, display a message and return
		if (player_level < lowestLevel) {
			String content = "<html><body>Come back here when you have reached level " + lowestLevel + ". I will give you support magic then.</body></html>";
			insertObjectIdAndShowChatWindow(player, content);
			return;
		}

		L2Skill skill = null;
		if (isSummon) {
			//			for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable()) {
			//				if (helperBuffItem.isForSummon()) {
			//					skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel());
			//					if (skill != null)
			//						doCast(skill);
			//				}
			//			}
		} else {
			// 	Go through the Helper Buff list define in sql table helper_buff_list and cast skill
			//			for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable()) {
			//				if (helperBuffItem.isMagicClassBuff() == player.isMageClass()) {
			//					if (player_level >= helperBuffItem.getLowerLevel() && player_level <= helperBuffItem.getUpperLevel()) {
			//						skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel());
			//						doCast(skill);
			//					}
			//				}
			//			}
		}
	}

	public void showChatWindow(FuzePcInstance player) {
		showChatWindow(player, 0);
	}

	/**
	 * Returns true if html exists
	 * 
	 * @param player
	 * @param type
	 * @return boolean
	 */
	private boolean showPkDenyChatWindow(FuzePcInstance player, String type) {
		String html = HtmCache.getInstance().getHtm("data/html/" + type + "/" + getNpcId() + "-pk.htm");

		if (html != null) {
			NpcHtmlMessage pkDenyMsg = new NpcHtmlMessage(getObjectId());
			pkDenyMsg.setHtml(html);
			player.sendPacket(pkDenyMsg);
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return true;
		}

		return false;
	}

	/**
	 * Open a chat window on client with the text of the L2NpcInstance.<BR>
	 * <BR>
	 * 在客户端打开对话窗口<BR>
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Get the text of the selected HTML file in function of the npcId and of the page number</li> <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li> <li>
	 * Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet</li><BR>
	 * 
	 * @param player
	 *            The L2PcInstance that talk with the L2NpcInstance
	 * @param val
	 *            The number of the page of the L2NpcInstance to display
	 * 
	 */
	public void showChatWindow(FuzePcInstance player, int val) {

		if (player.getKarma() > 0) {
			if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && this instanceof L2MerchantInstance) {
				if (showPkDenyChatWindow(player, "merchant"))
					return;
			} else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE && this instanceof FuzeWarehouseInstance) {
				if (showPkDenyChatWindow(player, "warehouse"))
					return;
			}
		}

		if ("L2Auctioneer".equals(getTemplate().type) && val == 0)
			return;

		int npcId = getTemplate()._id;

		String filename;
		switch (npcId) {
		default:
			// Get the text of the selected HTML file in function of the npcId and of the page number
			filename = (getHtmlPath(npcId, val));
			break;
		}

		// Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance 
		NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
		html.setFile(filename);

		//String word = "npc-"+npcId+(val>0 ? "-"+val : "" )+"-dialog-append";

		if (this instanceof L2MerchantInstance)
			if (Config.LIST_PET_RENT_NPC.contains(npcId))
				html.replace("_Quest", "_RentPet\">Rent Pet</a><br><a action=\"bypass -h npc_%objectId%_Quest");

		html.replace("%objectId%", String.valueOf(getObjectId()));
		player.sendPacket(html);

		// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
		player.sendPacket(ActionFailed.STATIC_PACKET);
	}

	/**
	 * Open a chat window on client with the text specified by the given file name and path,<BR>
	 * relative to the datapack root. <BR>
	 * <BR>
	 * Added by Tempy
	 * 
	 * @param player
	 *            The L2PcInstance that talk with the L2NpcInstance
	 * @param filename
	 *            The filename that contains the text to send
	 * 
	 */
	public void showChatWindow(FuzePcInstance player, String filename) {
		// Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance 
		NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
		html.setFile(filename);
		html.replace("%objectId%", String.valueOf(getObjectId()));
		player.sendPacket(html);

		// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
		player.sendPacket(ActionFailed.STATIC_PACKET);
	}

	/**
	 * Kill the L2NpcInstance (the corpse disappeared after 7 seconds).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Create a DecayTask to remove the corpse of the L2NpcInstance after 7 seconds</li> <li>Set target to null and cancel Attack or Cast</li> <li>Stop movement</li> <li>Stop HP/MP/CP Regeneration task</li> <li>Stop all active skills effects in progress on the L2Character</li> <li>Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform</li> <li>Notify L2Character AI</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2Attackable</li><BR>
	 * <BR>
	 * 
	 * @param killer
	 *            The L2Character who killed it
	 * 
	 */
	@Override
	public boolean doDie(L2Character killer) {
		if (!super.doDie(killer))
			return false;
		// 加入腐烂管理器
		DecayTaskManager.getInstance().addDecayTask(this);
		return true;
	}

	/**
	 * Set the spawn of the L2NpcInstance.<BR>
	 * <BR>
	 * 
	 * @param spawn
	 *            The L2Spawn that manage the L2NpcInstance
	 * 
	 */
	public void setSpawn(L2Spawn spawn) {
		_spawn = spawn;
	}

	@Override
	public void onSpawn() {
		if (_inventory != null)
			_inventory.reset();
		super.onSpawn();
		if (getTemplate().getEventQuests(Quest.QuestEventType.ON_SPAWN) != null)
			for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_SPAWN))
				quest.notifySpawn(this);
	}

	/**
	 * 腐烂处理（把对象从可见世界中删除，由腐烂管理器定期调用）.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove the L2NpcInstance from the world when the decay task is launched</li> <li>Decrease its spawn counter</li> <li>Manage Siege task (killFlag, killCT)</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
	 * <BR>
	 * 
	 */
	@Override
	public void onDecay() {
		if (isDecayed())
			return;
		setDecayed(true);
		// 把NPC对象从可见世界中删除掉
		super.onDecay();
		// 降低出生点的数量
		if (_spawn != null)
			_spawn.decreaseCount(this);
	}

	/**
	 * Remove PROPERLY the L2NpcInstance from the world.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove the L2NpcInstance from the world and update its spawn object</li> <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2NpcInstance then cancel Attack or Cast and notify AI</li> <li>Remove L2Object object from _allObjects of L2World</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
	 * <BR>
	 * 
	 */
	public void deleteMe() {
		stopAllEffects();
		//停止自恢复逻辑
		getStatus().stopRegeneration();
		try {
			decayMe();
		} catch (Exception e) {
			_log.error("Failed decayMe().", Config.SERVER_ID, e);
		}

		// Remove L2Object object from _allObjects of L2World
		FuzeWorld.getInstance().exitWorld(this);
	}

	/**
	 * Return the L2Spawn object that manage this L2NpcInstance.<BR>
	 * <BR>
	 */
	public L2Spawn getSpawn() {
		return _spawn;
	}

	@Override
	public String toString() {
		return getTemplate().name;
	}

	public boolean isDecayed() {
		return _isDecayed;
	}

	public void setDecayed(boolean decayed) {
		_isDecayed = decayed;
	}

	@Override
	public NpcInventory getInventory() {
		return _inventory;
	}

	@Override
	public void sendInfo(FuzePcInstance activeChar) {
		if (Config.CHECK_KNOWN)
			activeChar.sendMessage("Added NPC: " + this.getName());
		if (getAI() == null)
			activeChar.sendPacket(new ServerObjectInfo(this, activeChar));
		else
			activeChar.sendPacket(new AbstractNpcInfo.NpcInfo(this, activeChar));
	}
}
